import {
  Directive,
  Input,
  ElementRef,
  Renderer2,
  ViewContainerRef,
  ComponentFactory,
  ComponentFactoryResolver,
  TemplateRef,
  Output,
  EventEmitter,
  OnInit,
  OnChanges,
  OnDestroy,
  SimpleChanges,
  ChangeDetectorRef,
} from '@angular/core';
import { Subject } from 'rxjs';
import { LvConfig } from '../core/config';
import { InputBoolean, InputNumber } from '../core/utils/convert';
import { PositionType, TriggerType, ThemeType } from '../core/tooltipbase/api';
import { TooltipComponent } from './tooltip.component';

@Directive({
  selector: '[lv-tooltip], [lvTooltip]',
  exportAs: 'lvTooltip',
})
export class TooltipDirective implements OnChanges, OnInit, OnDestroy {
  @Input('lvTooltipIcon') lvIcon: string; // 内容图标

  @Input('lvTooltipClassName') lvClassName: string; // overlay样式类

  @Input('lvTooltipOuterClosable') lvOuterClosable = true; // 点击tooltip外部时是否关闭tooltip

  @Input('lvTooltipZIndex') @InputNumber() lvZIndex: number; // 层级

  @Input('lvTooltipTrigger') lvTrigger: TriggerType = 'hover'; // 触发方式

  @Input('lvTooltipPosition') lvPosition: PositionType = 'top'; // 展示位置，默认top，具体设置见POSITION_MAP

  @Input('lvTooltipOrigin') lvOrigin: ElementRef = this.elementRef; // overlay触发源点

  @Input('lv-tooltip') lvContent: string | TemplateRef<void> = null; // 提示内容

  @Input('lvTooltipVisible') @InputBoolean() lvVisible: boolean = false; // 控制overlay是否显示,lvTrigger为customize时生效

  @Input('lvTooltipShowArrow') @InputBoolean() lvShowArrow: boolean = true; // 控制overlay小三角是否显示

  @Input('lvTooltipTheme') lvTheme: ThemeType = LvConfig.tooltipOptions.lvTheme || 'light'; // 主题颜色

  @Input('lvTooltipArrowPointAtCenter') @InputBoolean() lvArrowPointAtCenter: boolean = false;

  @Output('lvTooltipExternalTrigger') lvExternalTrigger: EventEmitter<any> = new EventEmitter(); // 外部触发关闭事件

  @Input('lvTooltipScrollToClose') lvScrollToClose: boolean | { exclusions: HTMLElement[] } = true; // 滚动时是否隐藏

  @Output('lvTooltipVisibleChange') readonly lvVisibleChange: EventEmitter<boolean> = new EventEmitter();

  @Input('lvTooltipContextData') lvContextData: number | string | boolean | Array<any> | { [key: string]: any }; // 内容模板上下文数据

  tooltipRef;

  tooltip: TooltipComponent;
  /**
   * 用于动态创建tooltip组件，
   */
  protected ComponentFactory: ComponentFactory<TooltipComponent> =
    this.resolver.resolveComponentFactory(TooltipComponent);

  protected needProxyProperties = [
    'lvContent',
    'lvIcon',
    'lvZIndex',
    'lvTheme',
    'lvTrigger',
    'lvPosition',
    'lvScrollToClose',
    'lvOuterClosable',
    'lvClassName',
    'lvVisible',
    'lvShowArrow',
    'lvExternalTrigger',
    'lvVisibleChange',
    'lvOrigin',
    'lvContextData',
    'lvArrowPointAtCenter',
  ];

  private destroy$ = new Subject<void>();
  constructor(
    public elementRef: ElementRef,
    protected renderer: Renderer2,
    protected resolver: ComponentFactoryResolver,
    protected ViewContainer: ViewContainerRef,
    protected cdr: ChangeDetectorRef,
  ) {}

  show(): void {
    this.tooltip.show();
  }

  hide(): void {
    this.tooltip.hide();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  ngOnInit(): void {
    if (!this.tooltip) {
      this.createTooltipComponent();
    }
  }

  /**
   * 让组件更新其位置，用于鼠标移动时，改变tips位置
   */
  updatePosition(): void {
    if (this.tooltip) {
      this.tooltip.updatePosition();
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    Object.keys(changes).forEach((key): void => {
      if (changes[key]) {
        this.updateComponentProperties(key, changes[key].currentValue);
      }
    });

    this.cdr.markForCheck();
  }

  private removeTipElement(): void {
    if (this.tooltipRef?.location?.nativeElement) {
      // 删除组件的DOM，因为它应该在overlay容器中。
      this.renderer.setStyle(this.tooltipRef.location.nativeElement, 'display', 'none');
    }
  }

  /**
   * 创建tooltip组件
   */
  private createTooltipComponent(): void {
    this.tooltipRef = this.ViewContainer.createComponent(this.ComponentFactory);
    this.tooltip = this.tooltipRef.instance;
    this.tooltip.element = this.elementRef.nativeElement;

    this.removeTipElement();

    // 更新所有属性到组件
    this.needProxyProperties.forEach((property): void => {
      this.updateComponentProperties(property, this[property]);
    });
  }

  /**
   * 更新directive的属性到component
   */
  private updateComponentProperties(key: string, value: any): void {
    if (value !== null || value !== undefined) {
      if (key !== 'lvVisible' && key !== 'lvPosition' && key !== 'lvContent') {
        this.tooltip && (this.tooltip[key] = value);
      } else {
        if (key === 'lvVisible') {
          if (this.tooltip && value !== this.tooltip.lvVisible) {
            this.tooltip.visibleChange(value);
          }
        }

        if (key === 'lvPosition') {
          if (this.tooltip) {
            this.tooltip.positionChange(value);
          }
        }

        if (key === 'lvContent') {
          if (!this.tooltip) {
            return;
          }
          this.tooltip[key] = value;
          if (this.tooltip.isContentEmpty()) {
            this.hide();
          }
        }
      }
    }
  }
}
